package com.dhcc.bpm.common.redisMessage;

import com.alibaba.fastjson.JSON;
import com.dhcc.bpm.common.utils.SerializationUtil;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

@Service("common.RedisService")
public class RedisService<T> {


    @Resource
    private RedisTemplate<String, ?> redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 写入redis缓存
     *
     * @param key    key
     * @param value  value
     * @param expire 设置过期时间 小于0为不设置过期时间
     * @return 操作结果
     */
    public boolean set(final String key, final String value, final Long expire) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            connection.set(serializer.serialize(key), serializer.serialize(value));
            if (expire > 0) {
                connection.expire(serializer.serialize(key), expire);
            }
            return true;
        });
    }

    /**
     * 读取缓存
     *
     * @param key key
     * @return 结果
     */
    public String get(final String key) {
        return redisTemplate.execute((RedisCallback<String>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            byte[] value = connection.get(serializer.serialize(key));
            return serializer.deserialize(value);
        });
    }

    /**
     * Find all keys matching the given {@code pattern}.
     *
     * @param pattern must not be {@literal null}.
     * @return keys
     * @see <a href="http://redis.io/commands/keys">Redis Documentation: KEYS</a>
     */
    public List<String> keys(final String pattern) {
        return redisTemplate.execute((RedisCallback<List<String>>) redisConnection -> {
            List<String> res = new ArrayList<>();
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            Set<byte[]> keys = redisConnection.keys(serializer.serialize(pattern));
            for (byte[] key : keys) {
                res.add(serializer.deserialize(key));
            }
            return res;
        });
    }

    /**
     * Find all  values with the key matching the given {@code pattern}.
     *
     * @param pattern must not be {@literal null}.
     * @return values
     */
    public List<String> valuesForKeys(final String pattern) {
        return redisTemplate.execute((RedisCallback<List<String>>) connection -> {
            List<String> values = new ArrayList<>();
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            Set<byte[]> keys = connection.keys(serializer.serialize(pattern));
            for (byte[] key : keys) {
                byte[] value = connection.get(key);
                values.add(serializer.deserialize(value));
            }
            return values;
        });
    }

    /**
     * 删除指定key缓存内容
     *
     * @param key key
     * @return 操作结果
     */
    public boolean remove(final String key) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            connection.del(serializer.serialize(key));
            return true;
        });
    }

    /**
     * remove all  items with the key matching the given {@code pattern}.
     *
     * @param pattern must not be {@literal null}.
     * @return 操作结果
     */
    public boolean removeAll(final String pattern) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            Set<byte[]> keys = connection.keys(serializer.serialize(pattern));
            for (byte[] key : keys) {
                connection.del(key);
            }
            return true;
        });
    }

    /**
     * 写入hash类型数据
     *
     * @param key    key
     * @param filed  字段名
     * @param value  值
     * @param expire 设置过期时间 小于0为不设置过期时间
     * @return 操作结果
     */
    public Boolean hashSet(final String key, final String filed, final Object value, final long expire) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            boolean result = connection.hSet(serializer.serialize(key), serializer.serialize(filed), SerializationUtil.serialize(value));
            if (expire > 0) {
                connection.expire(serializer.serialize(key), expire);
            }
            return result;
        });
    }

    /**
     * 删除hash指定的字段值
     * @param key key
     * @param filed 字段名
     * @return value
     */
    public Object hashDel(final String key, final String filed){
        return redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                return connection.hDel(serializer.serialize(key), serializer.serialize(filed));
            }
        });
    }

    /**
     * 获取hash指定的字段值
     *
     * @param key   key
     * @param filed 字段名
     * @return value
     */
    public Object hashGet(final String key, final String filed) {
        return redisTemplate.execute((RedisCallback<Object>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            byte[] value = connection.hGet(serializer.serialize(key), serializer.serialize(filed));
            if (value==null)  return null;
            return serializer.deserialize(value);
            //return SerializationUtil.deserialize(value);
        });
    }

    /**
     * 获取指定key的所有hash类型数据
     *
     * @param key key
     * @return object
     */
    public Map<String, Object> hashGetAll(final String key) {
        return redisTemplate.execute((RedisCallback<Map<String, Object>>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            Map<byte[], byte[]> map = connection.hGetAll(serializer.serialize(key));
            Map<String, Object> resultMap = new HashMap<>();
            for (byte[] bytes : map.keySet()) {
                byte[] val = map.get(bytes);
                resultMap.put(serializer.deserialize(bytes), SerializationUtil.deserialize(val));
            }
            return resultMap;
        });
    }

    /**
     * 获取哈希表中所有值。
     * @param key key
     * @return object
     */
    public List<String> hvals(final String key) {
            return redisTemplate.execute((RedisCallback<List<String>>) connection -> {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                List<byte[]> keys =  connection.hVals(serializer.serialize(key));
                List<String> res = new ArrayList<>();
                for (byte[] bytes : keys) {
                    res.add(serializer.deserialize(bytes));
                }
                return res;
            });
    }
    /**
     * 根据reids key 获取集合
     *
     * @param key reids key
     * @param clz 返回类型
     * @return 集合
     */
    public List<T> hashGetAll(final String key, final Class clz) {
        List<T> list = new ArrayList<>();
        Map<String, Object> stringObjectMap = this.hashGetAll(key);
        for (Object redisOffer : stringObjectMap.values()) {
            T t = (T) JSON.parseObject(redisOffer.toString(), clz);
            list.add(t);
        }
        return list;
    }



    /**
     * redis list操作 从list的左边插入一条数据
     *
     * @param key   key
     * @param value value
     * @return 操作结果
     */
    public Boolean lpush(final String key, final String value) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
            connection.lPush(stringSerializer.serialize(key), stringSerializer.serialize(value));
            return true;
        });
    }


    /**
     * redis list操作 从list的右边取出数据
     *
     * @param key key
     * @return value
     */
    public String Rpop(final String key) {
        return redisTemplate.execute((RedisCallback<String>) connection -> {
            RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
            byte[] valueBytes = connection.rPop(stringSerializer.serialize(key));
            return stringSerializer.deserialize(valueBytes);
        });
    }

    /**
     * redis list 操作 队列长度
     *
     * @param key key
     * @return 队列长度
     */
    public Long llen(final String key) {
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
            return connection.lLen(stringSerializer.serialize(key));
        });
    }

    /**
     * redis list操作 根据范围从list 取出数据
     *
     * @param key
     * @param pos 起始位置 0  为开头
     * @param end 截止位置 -1 为结尾
     * @return
     */
    public List<String> Lrange(final String key, final long pos, final long end) {
        return stringRedisTemplate.opsForList().range(key, pos, end);
    }

    /**
     * redis 根据参数 count 的值，移除 list 中与参数 value 相等的元素
     *
     * @param key   key
     * @param count count > 0:从表头开始向表尾搜索，移除与 value 相等的元素，数量为 count,
     *              count < 0:从表尾开始向表头搜索，移除与 value 相等的元素，数量为 count 的绝对值
     *              count = 0:移除表中所有与 value 相等的值
     * @param value value
     * @return 从list中移除元素的数量，key不存在时，返回0
     */
    public Long lrem(final String key, final long count, final String value) {
        return redisTemplate.execute((RedisCallback<Long>) redisConnection -> {
            RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
            return redisConnection.lRem(stringSerializer.serialize(key), count, stringSerializer.serialize(value));
        });
    }


    /**
     * 批量保存数据到zset中
     *
     * @param key
     * @param tuples
     * @param expire
     * @return java.lang.Boolean
     * @author jim
     * @date 2018/5/2
     */
    public Boolean zSetAll(final String key, final Set<RedisZSetCommands.Tuple> tuples, final long expire) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            connection.zAdd(serializer.serialize(key), tuples);
            if (expire > 0) {
                connection.expire(serializer.serialize(key), expire);
            }
            return true;
        });
    }

    /**
     * 获取zset中的数据，包含开始和结束位置的数据
     *
     * @param key
     * @param start 开始位置，以0开始
     * @param end   结束位置
     * @return java.util.List<java.lang.Object>
     * @author jim
     * @date 2018/5/2
     */
    public List<Object> zGet(final String key, final long start, final long end) {
        return redisTemplate.execute((RedisCallback<List<Object>>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            List<Object> list = new ArrayList<>();
            Set<byte[]> set = connection.zRange(serializer.serialize(key), start, end);
            for (byte[] bytes : set) {
                list.add(SerializationUtil.deserialize(bytes));
            }
            return list;
        });
    }

    /**
     * 获取zset中value的数量
     *
     * @param key
     * @return java.lang.Long
     * @author jim
     * @date 2018/5/2
     */
    public Long zCard(final String key) {
        return redisTemplate.execute((RedisCallback<Long>) connection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            List<Object> list = new ArrayList<>();
            return connection.zCard(serializer.serialize(key));
        });
    }

    /**
     * 保存单个数据到set中
     *
     * @param key
     * @param value
     * @return 已存在时 返回0，否则返回插入数量
     */
    public long sAdd(final String key, final String value) {
        return redisTemplate.execute((RedisCallback<Long>) redisConnection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            return redisConnection.sAdd(serializer.serialize(key), serializer.serialize(value));
        });
    }

    /**
     * 判断value是否存在该set中
     *
     * @param key
     * @param value
     * @return 存在返回true 不存在返回false
     */
    public Boolean sIsMember(final String key, final String value) {
        return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            return redisConnection.sIsMember(serializer.serialize(key), serializer.serialize(value));
        });
    }

    /**
     * 从set中移除一个value元素
     *
     * @param key
     * @param value
     * @return
     */
    public long sRem(final String key, final String value) {
        return redisTemplate.execute((RedisCallback<Long>) redisConnection -> {
            RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
            return redisConnection.sRem(serializer.serialize(key), serializer.serialize(value));
        });
    }


    /**
     * setNX用来做分布式锁
     *
     * @param key   key
     * @param value value
     * @return
     */
    public Boolean setNx(final String key, final String value, final Long expire) {
        return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
            RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
            Boolean success = redisConnection.setNX(stringSerializer.serialize(key), stringSerializer.serialize(value));
            redisConnection.expire(stringSerializer.serialize(key), expire);
            return success;
        });
    }


    public String getSet(final String key, final String value) {
        return redisTemplate.execute((RedisCallback<String>) redisConnection -> {
            RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
            byte[] set = redisConnection.getSet(stringSerializer.serialize(key), stringSerializer.serialize(value));
            return stringSerializer.deserialize(set);
        });
    }



}
